<?php

namespace helper;


class Str
{

  /**
   * 对表情符号进行处理入库
   * @param string $content
   * @return string
   */
  static public function encodeLook($content = '')
  {
    return preg_replace_callback('/[\xf0-\xf7].{3}/', function ($r) {
      return '@E' . base64_encode($r[0]);
    }, $content);
  }

  /**
   * 返回内容的时候对 使用encodeLook加密过的表情进行转换
   * @param string $content
   * @return string
   */
  static public function decodeLook($content = '')
  {
    return preg_replace_callback('/@E(.{6}==)/', function ($r) {
      return base64_decode($r[1]);
    }, $content);
  }

  /**
   * authcode加密函数
   * @param $str //加减密字符串
   * @param string $operation
   * @param string $key
   * @param int $expiry
   * @return bool|string
   */
  static public function authcode($str, $operation = 'DECODE', $key = 'DmoZ2CeT', $expiry = 0)
  {
    $m   = ['+' => '75Rym', '/' => '9UqW3'];
    $str = str_replace(array_values($m), array_keys($m), $str);
    // 动态密匙长度，相同的明文会生成不同密文就是依靠动态密匙
    $ckey_length = 4;
    // 密匙
    $key = md5($key);
    // 密匙a会参与加解密
    $keya = md5(substr($key, 0, 16));
    // 密匙b会用来做数据完整性验证
    $keyb = md5(substr($key, 16, 16));
    // 密匙c用于变化生成的密文
    $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($str, 0, $ckey_length) :
        substr(md5(microtime()), -$ckey_length)) : '';
    // 参与运算的密匙
    $cryptKey   = $keya . md5($keya . $keyc);
    $key_length = strlen($cryptKey);
    // 明文，前10位用来保存时间戳，解密时验证数据有效性，10到26位用来保存$keyb(密匙b)，
    //解密时会通过这个密匙验证数据完整性
    // 如果是解码的话，会从第$ckey_length位开始，因为密文前$ckey_length位保存 动态密匙，以保证解密正确
    $str           = $operation == 'DECODE' ? base64_decode(substr($str, $ckey_length)) :
        sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($str . $keyb), 0, 16) . $str;
    $string_length = strlen($str);
    $result        = '';
    $box           = range(0, 255);
    $rickey        = array();
    // 产生密匙簿
    for ($i = 0; $i <= 255; $i++) {
      $rickey[$i] = ord($cryptKey[$i % $key_length]);
    }
    // 用固定的算法，打乱密匙簿，增加随机性，好像很复杂，实际上对并不会增加密文的强度
    for ($j = $i = 0; $i < 256; $i++) {
      $j       = ($j + $box[$i] + $rickey[$i]) % 256;
      $tmp     = $box[$i];
      $box[$i] = $box[$j];
      $box[$j] = $tmp;
    }
    // 核心加解密部分
    for ($a = $j = $i = 0; $i < $string_length; $i++) {
      $a       = ($a + 1) % 256;
      $j       = ($j + $box[$a]) % 256;
      $tmp     = $box[$a];
      $box[$a] = $box[$j];
      $box[$j] = $tmp;
      // 从密匙簿得出密匙进行异或，再转成字符
      $result .= chr(ord($str[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }
    if ($operation == 'DECODE') { //解密
      // 验证数据有效性，请看未加密明文的格式
      if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) &&
          substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) {
        return substr($result, 26);
      } else {
        return '';
      }
    } else { //加密
      // 把动态密匙保存在密文里，这也是为什么同样的明文，生产不同密文后能解密的原因
      // 因为加密后的密文可能是一些特殊字符，复制过程可能会丢失，所以用base64编码
      $result = str_replace('=', '', base64_encode($result));
      $result = str_replace(array_keys($m), array_values($m), $result); //去除混淆字符
      return $keyc . $result;
    }
  }

  /**
   * 获取指定长度的随机字母数字组合的字符串
   * @param int $length
   * @param int $type
   * @param string $addChars
   * @return string
   */
  static public function random($length = 6, $type = null, $addChars = '')
  {
    $str = '';
    switch ($type) {
      case 0:
        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' . $addChars;
        break;
      case 1:
        $chars = str_repeat('0123456789', 3);
        break;
      case 2:
        $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars;
        break;
      case 3:
        $chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars;
        break;
      case 4:
        $chars = "们以我到他会作时要动国产的一是工就年阶义发成部民可出能方进在了不和有大这主中人上为来分生对于学下级地个用同行面说种过命度革而多子后自社加小机也经力线本电高量长党得实家定深法表着水理化争现所二起政三好十战无农使性前等反体合斗路图把结第里正新开论之物从当两些还天资事队批点育重其思与间内去因件日利相由压员气业代全组数果期导平各基或月毛然如应形想制心样干都向变关问比展那它最及外没看治提五解系林者米群头意只明四道马认次文通但条较克又公孔领军流入接席位情运器并飞原油放立题质指建区验活众很教决特此常石强极土少已根共直团统式转别造切九你取西持总料连任志观调七么山程百报更见必真保热委手改管处己将修支识病象几先老光专什六型具示复安带每东增则完风回南广劳轮科北打积车计给节做务被整联步类集号列温装即毫知轴研单色坚据速防史拉世设达尔场织历花受求传口断况采精金界品判参层止边清至万确究书" . $addChars;
        break;
      default:
        $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars;
        break;
    }
    if ($length > 10) {
      $chars = $type == 1 ? str_repeat($chars, $length) : str_repeat($chars, 5);
    }
    if ($type != 4) {
      $chars = str_shuffle($chars);
      $str   = substr($chars, 0, $length);
    } else {
      for ($i = 0; $i < $length; $i++) {
        $str .= mb_substr($chars, floor(mt_rand(0, mb_strlen($chars, 'utf-8') - 1)), 1);
      }
    }
    return $str;
  }

  /**
   * 字符串*号替换
   * @param string $string 需要替换的字符串
   * @param int $start 开始的保留几位
   * @param int $end 最后保留几位
   * @return string
   */
  static public function strMiddleReplace($string, $start, $end)
  {
    $p = substr($string, 0, $start) . "****" . substr($string, -1 * $end, $end);
    return $p;
  }


  /**
   * 字符串截取，支持中文和其他编码
   * @param string $str 源字符串
   * @param int $start 从哪里开始
   * @param int $length 截取的长度
   * @param string $charset 编码，默认utf-8
   * @param bool $suffix 尾部是否添加···,默认添加
   * @return bool|string 截取好的字符串
   */
  static public function msubstr($str, $start, $length, $charset = "utf-8", $suffix = true)
  {
    if (function_exists("mb_substr"))
      $slice = mb_substr($str, $start, $length, $charset);
    elseif (function_exists('iconv_substr')) {
      $slice = iconv_substr($str, $start, $length, $charset);
      if (false === $slice) {
        $slice = '';
      }
    } else {
      $re['utf-8']  = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
      $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
      $re['gbk']    = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
      $re['big5']   = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
      preg_match_all($re[$charset], $str, $match);
      $slice = join("", array_slice($match[0], $start, $length));
    }
    if ($suffix and mb_strlen($str, 'utf8') > $length) {
      return $slice . '...';
    } else {
      return $slice;
    }
  }

  /**
   * 提取字符串内容中的图片地址
   * @param string $str HTML或者文章
   * @param int $number 共抽取 $number 张；默认为0，为0则抽取到最后;
   * @param string $order 顺取倒取； 默认为 asc ，从正方向计数。 desc 从反方向计数
   * @param bool $blacklist 图片地址黑名单，排除图片地址中包含该数据的地址；例如 传入 img.baidu.com
   * @return array|string
   */
  static public function extractImg($str, $number = 0, $order = 'asc', $blacklist = false)
  {
    preg_match_all("/<img.*>/isU", $str, $ereg);
    $img = $ereg[0];
    if ($order != 'asc') $img = array_reverse($img);
    $imgSrcArr = [];
    foreach ($img as $key => $value) {
      $imgSrc    = $value;
      $pregModel = "/src=('|\")(.*)('|\")/isU";
      preg_match_all($pregModel, $imgSrc, $img1);
      if (is_array($blacklist)) {
        $blacklistBool = true;
        foreach ($blacklist as $v) {
          if (strpos($img1[2][0], $v) !== false) $blacklistBool = false;
        }
        if ($blacklistBool) {
          $imgSrcArr[] = $img1[2][0];
        };
      } else {
        if (strpos($img1[2][0], $blacklist) === false) {
          $imgSrcArr[] = $img1[2][0];
        };
      }
    }
    if ($number > 0) {
      return array_slice($imgSrcArr, 0, $number);
    } else {
      return array_slice($imgSrcArr, 0);
    }
  }

  /**
   * textarea文本转化数组
   * @param $str
   * @return array
   */
  static public function textarea2Arr($str)
  {
    if (empty($str)) return [];
    $str = nl2br($str);
    $str = explode("<br />", $str);
    $arr = [];
    foreach ($str as $v) {
      $arr[] = trim($v);
    }
    return $arr;
  }

  /**
   * 生成唯一订单号
   * @param string $prefix
   * @return string
   */
  static public function builderOrderSn($prefix = '')
  {
    $prefix = !empty($prefix) ? $prefix : '';
    return $prefix . date('Ymd') .
        substr(microtime(), 2, 5) .
        substr(implode(NULL,
            array_map('ord', str_split(substr(uniqid($prefix), 7, 13), 1))
        ), 0, 8) .
        sprintf('%04d', rand(0, 9999));
  }


  /**
   * 生成UUID
   * @param string $prefix
   * @return string
   * @throws \Exception
   */
  static public function uuid($prefix = '')
  {
    $charId = strtoupper(md5($prefix . uniqid(mt_rand(), true)));
    $hyphen = chr(45);// "-"
    return substr($charId, 0, 8) . $hyphen
        . substr($charId, 8, 4) . $hyphen
        . substr($charId, 12, 4) . $hyphen
        . substr($charId, 16, 4) . $hyphen
        . substr($charId, 20, 12);
  }

}
